#include "display/game/GameGraphic.hpp"

GameGraphic::GameGraphic(ThreadDisplay * p_screenManager) :
m_mutexPlayers(0), 		m_mutexShot(0), 		m_players(),			m_displayScore(false),
m_gameEngine(0), 		m_map(0), 				m_shots(),				Displayable(p_screenManager),
m_networkManager(0),	m_directionH(Character::None),					m_directionV(Character::None)
{
	this->m_mutexPlayers	= new sf::Mutex();
	this->m_mutexShot 		= new sf::Mutex();

	this->InitGame();

	m_networkManager 	= new Client(this->m_gameEngine);
	m_map 				= new MapGraphique	(this->m_gameEngine->GetMap());

	if( ! m_networkManager->StartClient(GameInfo::m_PlayerName, GameInfo::m_IP))
	{
		//Debug::printTrace(Debug::information, "display/game/GameGraphic.constructor", "back to mainscreen");
		this->BackToMainScreen();
	}
	//Debug::printTrace(Debug::information, "display/game/GameGraphic.constructor", "");
}

GameGraphic::~GameGraphic()
{
	//Debug::printTrace(Debug::information, "display/game/GameGraphic.Destructor", "");
	m_gameEngine->DeletteObserver(this);

	delete this->m_networkManager;
	delete this->m_map;
	delete this->m_mutexPlayers;
	delete this->m_mutexShot;
	delete this->m_gameEngine;
}
/**
 * Init the GameEngine
 */
void GameGraphic::InitGame()
{
	this->m_gameEngine = new GameEngine(false);
	this->m_gameEngine->AddObserver(this);

	//TODO Erase when server running
	/*Character* mycharacter = m_gameEngine->AddPlayer();
	mycharacter->SetName("Copain1");*/

	this->m_gameEngine->Launch();
}

/**
 * Draw all element inside the game
 */
void GameGraphic::draw(sf::RenderWindow* window)
{
	m_map->draw(window);

	//display players
	this->m_mutexPlayers->Lock();
	for (unsigned int i(0) ; i < this->m_players.size() ; i++)
	{
		m_players[i]->draw(window);
	}
	this->m_mutexPlayers->Unlock();

	//display bullets
	this->m_mutexShot->Lock();
	for (unsigned int i(0) ; i < this->m_shots.size() ; i++)
	{
		m_shots[i]->draw(window);
	}
	this->m_mutexShot->Unlock();

	// Scores
	this->DisplayScores(window);
}
/**
 * Caught the event from the gameEngine (New players, shots etc...)
 */
void GameGraphic::Notify(int code, void* parameter)
{
	switch(code)
	{
	case GameEngine::NOTIFICATION_CODE_NEW_PLAYER :
		this->NewUser((Character*)parameter);
		break;
	case GameEngine::NOTIFICATION_CODE_DELETE_PLAYER :
		this->DeleteUser((Character*)parameter);
		break;
	case GameEngine::NOTIFICATION_CODE_NEW_SHOT :
		this->NewShot((Shot*)parameter);
		break;
	case GameEngine::NOTIFICATION_CODE_DELETE_SHOT :
		this->DeleteShot((Shot*)parameter);
		break;
	}
}
/**
 * Create a Graphic player
 */
void GameGraphic::NewUser(Character* player)
{
	//Debug::printTrace(Debug::information, "GameGraphic.newUser", "");
	this->m_mutexPlayers->Lock();

	this->m_players.insert(this->m_players.end(), new CharacterGraphic(player));

	this->m_mutexPlayers->Unlock();
}
/**
 * Remove a GraphicPlayer
 */
void GameGraphic::DeleteUser(Character* player)
{
	//Debug::printTrace(Debug::information, "GameGraphic.DeleteUser", "");
	this->m_mutexPlayers->Lock();

	for (unsigned int i(0) ; i < this->m_players.size() ; i++)
	{
		if(player == m_players[i]->GetCharacter())
		{
			delete m_players[i];
			m_players.erase(m_players.begin() + i);
			break;
		}
	}
	this->m_mutexPlayers->Unlock();
}

/**
 * Remove a GraphicShot
 */
void GameGraphic::DeleteShot(Shot* p_shot)
{
	//Debug::printTrace(Debug::information, "GameGraphic.DeleteUser", "");
	this->m_mutexShot->Lock();

	for (unsigned int i(0) ; i < this->m_shots.size() ; i++)
	{
		if(p_shot == m_shots[i]->GetShot())
		{
			delete m_shots[i];
			m_shots.erase(m_shots.begin() + i);
			break;
		}
	}
	this->m_mutexShot->Unlock();
}

/**
 * Create a Graphic shot
 */
void GameGraphic::NewShot(Shot* p_shot)
{
	//Debug::printTrace(Debug::information, "GameGraphic.newUser", "");
	this->m_mutexShot->Lock();

	this->m_shots.insert(this->m_shots.end(), new ShotGraphic(p_shot));

	this->m_mutexShot->Unlock();
}
/**
 * catch keyboard event
 */
void GameGraphic::DoEvent(sf::Event e)
{
	//Debug::printTrace(Debug::information, "GameGraphic.DoEvent", "COCO");
	if ((e.Type == sf::Event::KeyPressed))
	{
		if(e.Key.Code == sf::Key::Z)
		{
			this->ChangeDirection(Character::North, m_directionH);
		}
		else if(e.Key.Code == sf::Key::S)
		{
			this->ChangeDirection(Character::South, m_directionH);
		}
		else if(e.Key.Code == sf::Key::Q)
		{
			this->ChangeDirection(m_directionV, Character::West);
		}
		else if(e.Key.Code == sf::Key::D)
		{
			this->ChangeDirection(m_directionV, Character::East);
		}
		else if(e.Key.Code == sf::Key::Tab)
		{
			this->m_displayScore = true;
		}
		else if(e.Key.Code == sf::Key::Escape)
		{
			this->BackToMainScreen();
		}
	}
	else if ((e.Type == sf::Event::KeyReleased))
	{
		if(e.Key.Code == sf::Key::Z)
		{
			if(m_directionV == Character::North)
				this->ChangeDirection(Character::None, m_directionH);
		}
		else if(e.Key.Code == sf::Key::S)
		{
			if(m_directionV == Character::South)
				this->ChangeDirection(Character::None, m_directionH);
		}
		else if(e.Key.Code == sf::Key::Q)
		{
			if(m_directionH == Character::West)
				this->ChangeDirection(m_directionV, Character::None);
		}
		else if(e.Key.Code == sf::Key::D)
		{
			if(m_directionH == Character::East)
				this->ChangeDirection(m_directionV, Character::None);
		}
		else if(e.Key.Code == sf::Key::Tab)
		{
			this->m_displayScore = false;
		}
	}


}
/**
 * Check if the direction change and notify the server if necessary
 */
void GameGraphic::ChangeDirection(int p_directionV, int p_directionH){
	if(p_directionH != m_directionH || p_directionV != m_directionV)
	{
		this->m_directionH = p_directionH;
		this->m_directionV = p_directionV;

		this->m_networkManager->SendNewDirection(m_directionV, m_directionH);
	}
}
/**
 * Do event which need continuous control for example shot whith the mouse
 */
void GameGraphic::DoRealTimeEvent(const sf::Input& p_event){
	if(p_event.IsMouseButtonDown(sf::Mouse::Left)){
		for (unsigned int i(0) ; i < this->m_players.size() ; i++)
		{
			//Debug::printTrace(Debug::information, "GameGraphic.DoRealTimeEvent", "Try SHOOT");
			if(m_players[i]->GetCharacter()->CanShoot())
			{
				//Debug::printTrace(Debug::information, "GameGraphic.DoRealTimeEvent", "SHOOT");
				//m_gameEngine->Shoot(m_players[i]->GetCharacter(), sf::Vector2f(p_event.GetMouseX(), p_event.GetMouseY()));
				this->m_networkManager->SendNewShot(p_event.GetMouseX(), p_event.GetMouseY());
			}
		}
	}
}
/**
 * Back to the main screen
 */
void GameGraphic::BackToMainScreen()
{
	GameScreen* menu = new GameScreen(this->GetScreenManager());

	this->GetScreenManager()->setDisplayable(menu);
}
/**
 * Display players score if m_displayScore = true
 */
void GameGraphic::DisplayScores(sf::RenderWindow* p_window)
{
	if(this->m_displayScore)
	{
		int l_nbRow = 0;
		sf::String* l_name 	= new sf::String();
		sf::String* l_kill 	= new sf::String();
		sf::String* l_death = new sf::String();

		sf::Shape l_rect   = sf::Shape::Rectangle(
				sf::Vector2f(
						GraphicValue::TABLE_SCORE_BORDER,
						GraphicValue::TABLE_SCORE_BORDER),
						sf::Vector2f(
								DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER,
								DefaultValue::MAP_SIZE_Y - GraphicValue::TABLE_SCORE_BORDER),
								sf::Color(0,0,255,128));

		sf::Shape l_lineH = sf::Shape::Line(
				sf::Vector2f(
						GraphicValue::TABLE_SCORE_BORDER,
						GraphicValue::TABLE_SCORE_BORDER + GraphicValue::TABLE_SCORE_HEIGHT_ROW),
						sf::Vector2f(
								DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER,
								GraphicValue::TABLE_SCORE_BORDER + GraphicValue::TABLE_SCORE_HEIGHT_ROW),
								1,
								sf::Color(255,255,255,128));
		sf::Shape l_lineV1 = sf::Shape::Line(
				sf::Vector2f(
						DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER * 2,
						GraphicValue::TABLE_SCORE_BORDER),
						sf::Vector2f(
								DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER * 2,
								DefaultValue::MAP_SIZE_Y - GraphicValue::TABLE_SCORE_BORDER),
								2,
								sf::Color(0,0,0,128));
		sf::Shape l_lineV2 = sf::Shape::Line(
				sf::Vector2f(
						DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER,
						GraphicValue::TABLE_SCORE_BORDER),
						sf::Vector2f(
								DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER,
								DefaultValue::MAP_SIZE_Y - GraphicValue::TABLE_SCORE_BORDER),
								2,
								sf::Color(0,0,0,128));

		p_window->Draw(l_rect);
		p_window->Draw(l_lineH);
		p_window->Draw(l_lineV1);
		p_window->Draw(l_lineV2);

		l_name ->SetText("Name:");
		l_kill ->SetText("Kill:");
		l_death->SetText("Death:");

		this->DisplayScoreRow(p_window, l_nbRow++, l_name, l_kill, l_death);

		this->m_mutexPlayers->Lock();
		for (unsigned int i(0) ; i < this->m_players.size() ; i++)
		{
			l_name ->SetText(m_players[i]->GetCharacter()->GetName());
			l_kill ->SetText(Text::float2string(m_players[i]->GetCharacter()->GetKill()));
			l_death->SetText(Text::float2string(m_players[i]->GetCharacter()->GetDeath()));
			this->DisplayScoreRow(p_window, l_nbRow++, l_name, l_kill, l_death);
		}
		this->m_mutexPlayers->Unlock();

		delete l_name;
		delete l_kill;
		delete l_death;
	}
}
/**
 * Display a score row on the good place
 */
void GameGraphic::DisplayScoreRow (sf::RenderWindow* p_window, int p_nbRow, sf::String* p_name, sf::String* p_kill, sf::String* p_death)
{
	p_name ->SetColor(sf::Color::White);
	p_kill ->SetColor(sf::Color::White);
	p_death->SetColor(sf::Color::White);

	p_name ->SetSize(GraphicValue::TABLE_SCORE_POLICE_SIZE);
	p_kill ->SetSize(GraphicValue::TABLE_SCORE_POLICE_SIZE);
	p_death->SetSize(GraphicValue::TABLE_SCORE_POLICE_SIZE);

	int l_height = GraphicValue::TABLE_SCORE_BORDER + GraphicValue::TABLE_SCORE_HEIGHT_ROW * p_nbRow;

	p_name ->SetY(l_height);
	p_kill ->SetY(l_height);
	p_death->SetY(l_height);

	p_name ->SetX(GraphicValue::TABLE_SCORE_BORDER + GraphicValue::TABLE_SCORE_BORDER_LEFT);
	p_kill ->SetX(DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER * 2 + GraphicValue::TABLE_SCORE_BORDER_LEFT);
	p_death->SetX(DefaultValue::MAP_SIZE_X - GraphicValue::TABLE_SCORE_BORDER - GraphicValue::TABLE_SCORE_WIDTH_COLUMN_COUNTER + GraphicValue::TABLE_SCORE_BORDER_LEFT);

	p_window->Draw(*p_name);
	p_window->Draw(*p_kill);
	p_window->Draw(*p_death);
}

